package top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.model.entity.AccessTokenEntity;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.model.entity.RefreshTokenEntity;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.model.valobj.AuthToken;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.model.valobj.UserType;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.repository.IOauthTokenRepository;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.service.IOauthTokenService;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.service.IPermissionService;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.security.model.UserInfo;
import top.yaofengqiao.springcloudsimple.common.constant.AuthConstant;
import top.yaofengqiao.springcloudsimple.common.constant.ResponseCode;
import top.yaofengqiao.springcloudsimple.common.util.AppException;
import top.yaofengqiao.springcloudsimple.redis.starter.service.IRedisService;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

/**
 * @author yfq
 * @date 2024/6/6 16:22
 * @description
 */
@Slf4j
@Service
public class OauthTokenService implements IOauthTokenService {
    @Resource
    private IRedisService redisService;
    @Resource
    private IOauthTokenRepository oauthTokenRepository;
    @Resource
    private IPermissionService permissionService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AuthToken createAuthToken(UserInfo userInfo, UserType userType, String clientId) {
        // 1. 创建refreshToken
        RefreshTokenEntity refreshTokenEntity = buildRefreshTokenEntity(userInfo.getUserId(), userType, clientId);
        boolean refreshTokenCreated = oauthTokenRepository.createRefreshToken(refreshTokenEntity);
        if (!refreshTokenCreated) {
            log.error("refreshToken创建失败：userId:{}，userType:{}，clientId:{}", userInfo.getUserId(), userType, clientId);
            throw new AppException(ResponseCode.UN_ERROR);
        }

        // 2. 创建accessToken
        AccessTokenEntity accessTokenEntity = buildAccessTokenEntity(userInfo.getUserId(), userType, clientId, refreshTokenEntity.getRefreshToken());
        boolean accessToken = oauthTokenRepository.createAccessToken(accessTokenEntity);
        if (!accessToken) {
            log.error("accessToken创建失败：userId:{}，userType:{}，clientId:{}", userInfo.getUserId(), userType, clientId);
            throw new AppException(ResponseCode.UN_ERROR);
        }

        // 3. 查询用户roles
        List<String> roles = userType == UserType.ADMIN ?
                permissionService.queryRoles(userInfo.getUserId()) : Collections.singletonList("member");

        // 4. 将token保存到redis
        String key = AuthConstant.RedisConstant.AUTH_TOKENS_PREFIX + accessTokenEntity.getAccessToken();
        AuthToken authToken = buildAuthToken(userInfo, userType, roles, accessTokenEntity);
        redisService.setValue(key, authToken, Duration.ofMinutes(AuthConstant.ACCESS_TOKEN_EXPIRE));

        return authToken;
    }

    @Override
    public RefreshTokenEntity queryRefreshToken(String refreshToken) {
        return oauthTokenRepository.queryRefreshToken(refreshToken);
    }

    @Override
    public AccessTokenEntity queryAccessToken(String refreshToken) {
        return oauthTokenRepository.queryAccessToken(refreshToken);
    }

    @Override
    public void invalidedRefreshToken(String refreshToken) {
        // 1. 查询accessToken
        AccessTokenEntity accessTokenEntity = queryAccessToken(refreshToken);

        // 2. 失效accessToken
        String key = AuthConstant.RedisConstant.AUTH_TOKENS_PREFIX + accessTokenEntity.getAccessToken();
        redisService.delete(key);

        // 3. TODO 失效refreshToken

    }

    private AuthToken buildAuthToken(UserInfo userInfo, UserType userType, List<String> roles, AccessTokenEntity accessTokenEntity) {
        return AuthToken.builder()
                .userId(userInfo.getUserId())
                .userType(userType.getType())
                .username(userInfo.getUsername())
                .icon(userInfo.getIcon())
                .mobile(userInfo.getMobile())
                .roles(roles)
                .accessToken(accessTokenEntity.getAccessToken())
                .refreshToken(accessTokenEntity.getRefreshToken())
                .clientId(accessTokenEntity.getClientId())
                .expiresTime(accessTokenEntity.getExpiresTime())
                .scopes("all")
                .build();
    }

    private RefreshTokenEntity buildRefreshTokenEntity(long userId, UserType userType, String clientId) {
        String refreshToken = UUID.randomUUID().toString();
        return RefreshTokenEntity.builder()
                .userId(userId)
                .refreshToken(refreshToken)
                .userType(userType.getType())
                .clientId(clientId)
                .expiresTime(LocalDateTime.now().plusMinutes(AuthConstant.ACCESS_TOKEN_EXPIRE))
                .build();
    }

    private AccessTokenEntity buildAccessTokenEntity(long userId, UserType userType, String clientId, String refreshToken) {
        String accessToken = UUID.randomUUID().toString();
        return AccessTokenEntity.builder()
                .userId(userId)
                .userType(userType.getType())
                .accessToken(accessToken)
                .refreshToken(refreshToken)
                .clientId(clientId)
                .expiresTime(LocalDateTime.now().plusMinutes(AuthConstant.REFRESH_TOKEN_EXPIRE))
                .build();
    }

}
